package org.wisdom.data.jpa.config;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

import java.util.Locale;

/**
 * 数据表和字段命名策略
 *
 * @author zhangzhiyan
 * @date 2021/07/06 17:08
 **/
public class DataNamingStrategy implements PhysicalNamingStrategy {
    private static final String TABLE_PREFIX = "t_";
    private static final String COLUMN_PREFIX = "c_";

    @Override
    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return this.apply(TABLE_PREFIX, identifier);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return this.apply(TABLE_PREFIX, identifier);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return this.apply(TABLE_PREFIX, identifier);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return this.apply(TABLE_PREFIX, identifier);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
        return this.apply(COLUMN_PREFIX, identifier);
    }

    private Identifier apply(String prefix, Identifier identifier) {
        Identifier wisdomIdentifier = this.apply(identifier);
        if (wisdomIdentifier != null) {
            String name = prefix + wisdomIdentifier.getText().toLowerCase(Locale.ROOT);
            return new Identifier(name, identifier.isQuoted());
        } else {
            return identifier;
        }
    }

    private Identifier apply(Identifier name) {
        if (name == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
        int n = 1;
        int m = builder.length() - 1;
        while (n < m){
            if (isUnderscoreRequired(builder.charAt(n - 1), builder.charAt(n), builder.charAt(n + 1))) {
                builder.insert(n, '_');
                n++;
                m++;
            }
            n++;
        }
        return getIdentifier(builder.toString(), name.isQuoted());
    }

    /**
     * Get an identifier for the specified details. By default this method will return an identifier with the name
     * adapted based on the result of
     * 
     * @param name
     *            the name of the identifier
     * @param quoted
     *            if the identifier is quoted
     * @return an identifier instance
     */
    private Identifier getIdentifier(String name, boolean quoted) {
        name = name.toLowerCase(Locale.ROOT);
        return new Identifier(name, quoted);
    }

    private boolean isUnderscoreRequired(char before, char current, char after) {
        return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
    }

}
